package com.umeox.moto.mqtt.mode;

import android.util.Log;

import com.umeox.moto.mqtt.BuildConfig;

import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory;
import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModule;

import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

public class UmeoxMqttAsyncClient extends MqttAsyncClient {

    public UmeoxMqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
        super(serverURI, clientId, persistence);
    }

    private int validateURI(String srvURI) {
        try {
            URI vURI = new URI(srvURI);
            if ("tcp".equals(vURI.getScheme())) {
                return 0;
            } else if ("ssl".equals(vURI.getScheme())) {
                return 1;
            } else if ("local".equals(vURI.getScheme())) {
                return 2;
            } else if ("ws".equals(vURI.getScheme())) {
                return 3;
            } else if ("wss".equals(vURI.getScheme())) {
                return 4;
            } else if (vURI.getPath() != null && !vURI.getPath().isEmpty()) {
                throw new IllegalArgumentException(srvURI);
            } else {
                throw new IllegalArgumentException(srvURI);
            }
        } catch (URISyntaxException var2) {
            throw new IllegalArgumentException(srvURI);
        }
    }

    @Override
    protected NetworkModule[] createNetworkModules(String defUri, MqttConnectOptions options) throws MqttException {
        NetworkModule[] networkModules;
        String[] tempSrvURIs = options.getServerURIs();
        String[] srvURIs;
        if (tempSrvURIs == null) {
            srvURIs = new String[]{defUri};
        } else if (tempSrvURIs.length == 0) {
            srvURIs = new String[]{defUri};
        } else {
            srvURIs = tempSrvURIs;
        }
        networkModules = new NetworkModule[srvURIs.length];
        for (int i = 0; i < srvURIs.length; i++) {
            networkModules[i] = this.createNetworkModule(srvURIs[i], options);
        }
        return networkModules;
    }

    private NetworkModule createNetworkModule(String srvURI, MqttConnectOptions options) throws MqttException {
        if (BuildConfig.DEBUG) {
            Log.d("UmeoxMqttAsyncClient", "createNetworkModule  address=" + srvURI);
        }

        SocketFactory socketFactory = options.getSocketFactory();
        int protocol = validateURI(srvURI);

        URI uri;
        try {
            uri = new URI(srvURI);
            String lowLine = "_";
            if (uri.getHost() == null && srvURI.contains(lowLine)) {
                try {
                    Field field = URI.class.getDeclaredField("host");
                    field.setAccessible(true);
                    String host = srvURI.substring(uri.getScheme().length() + 3);
                    field.set(uri, createNetworkModule(host));
                } catch (SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) {
                    throw ExceptionHelper.createMqttException(e.getCause());
                }
            }
        } catch (URISyntaxException e1) {
            throw new IllegalArgumentException("Malformed URI: " + srvURI + ", " + e1.getMessage());
        } catch (MqttException e1) {
            throw new IllegalArgumentException("Malformed URI: " + srvURI + ", " + e1.getMessage());
        }

        String host = uri.getHost();
        int port = uri.getPort();
        TCPNetworkModule networkModule = null;
        SSLSocketFactoryFactory sslSocketFactory = null;
        String[] cipherSuites;
        switch (protocol) {
            case 0:
                if (port == -1) {
                    port = 1883;
                }
                if (socketFactory == null) {
                    socketFactory = SocketFactory.getDefault();
                } else if (socketFactory instanceof SSLSocketFactory) {
                    throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
                }
                networkModule = new TCPNetworkModule(socketFactory, host, port, this.getClientId());
                networkModule.setConnectTimeout(options.getConnectionTimeout());
                break;
            case 1:
                if (BuildConfig.DEBUG) {
                    Log.i("UmeoxMqttAsyncClient", "createNetworkModule useTLS host=" + host + ", port=" + port);
                }
                if (port == -1) {
                    port = 8883;
                }

                if (socketFactory == null) {
                    sslSocketFactory = new SSLSocketFactoryFactory();
                    if (options.getSSLProperties() != null) {
                        sslSocketFactory.initialize(options.getSSLProperties(), null);
                    }
                    socketFactory = sslSocketFactory.createSocketFactory(null);
                } else if (!(socketFactory instanceof SSLSocketFactory)) {
                    throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
                }

                networkModule = new SSLNetworkModule((SSLSocketFactory) socketFactory, host, port, this.getClientId());
                ((SSLNetworkModule) networkModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
                ((SSLNetworkModule) networkModule).setSSLHostnameVerifier(options.getSSLHostnameVerifier());
                if (sslSocketFactory != null) {
                    cipherSuites = sslSocketFactory.getEnabledCipherSuites(null);
                    if (cipherSuites != null) {
                        ((SSLNetworkModule) networkModule).setEnabledCiphers(cipherSuites);
                    }
                }
                break;
            case 2:
            default:
                break;
            case 3:
                if (port == -1) {
                    port = 80;
                }

                if (socketFactory == null) {
                    socketFactory = SocketFactory.getDefault();
                } else if (socketFactory instanceof SSLSocketFactory) {
                    throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
                }

                networkModule = new WebSocketNetworkModule(socketFactory, srvURI, host, port, this.getClientId(),null);
                networkModule.setConnectTimeout(options.getConnectionTimeout());
                break;
            case 4:
                if (port == -1) {
                    port = 443;
                }

                if (socketFactory == null) {
                    sslSocketFactory = new SSLSocketFactoryFactory();
                    if (options.getSSLProperties() != null) {
                        sslSocketFactory.initialize(options.getSSLProperties(), null);
                    }
                    socketFactory = sslSocketFactory.createSocketFactory(null);
                } else if (!(socketFactory instanceof SSLSocketFactory)) {
                    throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
                }

                networkModule = new WebSocketSecureNetworkModule((SSLSocketFactory) socketFactory, srvURI, host, port, this.getClientId(),null);
                ((WebSocketSecureNetworkModule) networkModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
                if (sslSocketFactory != null) {
                    cipherSuites = sslSocketFactory.getEnabledCipherSuites(null);
                    if (cipherSuites != null) {
                        ((SSLNetworkModule) networkModule).setEnabledCiphers(cipherSuites);
                    }
                }
        }
        return networkModule;
    }

    private String createNetworkModule(String host) {
        int index = host.indexOf(0x3A);//Unicode Character ':'
        if (index == -1) {
            index = host.indexOf(0x2F);//Unicode Character '/'
        }
        if (index == -1) {
            index = host.length();
        }
        return host.substring(0, index);
    }
}
